<template>
	<Select @dropdownVisibleChange="handleFetch" v-bind="attrs" @change="handleChange" :options="getOptions">
		<template #[item]="data" v-for="item in Object.keys($slots)">
			<slot :name="item" v-bind="data || {}"></slot>
		</template>
		<template #suffixIcon v-if="loading">
			<LoadingOutlined spin />
		</template>
		<template #notFoundContent v-if="loading">
			<span> <LoadingOutlined spin class="mr-1" />暂无数据 </span>
		</template>
	</Select>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue';
import { Select } from 'ant-design-vue';
import { isFunction } from '@/utils/is';
import { get, omit } from 'lodash-es';
import { LoadingOutlined } from '@ant-design/icons-vue';
import { useAttrs } from '@/hooks/core/useAttrs';

type OptionsItem = { label: string; value: string; disabled?: boolean };

export default defineComponent({
	name: 'ApiSelect',
	components: {
		Select,
		LoadingOutlined,
	},
	props: {
		numberToString: { type: Boolean as PropType<boolean>, default: null },
		api: {
			type: Function as PropType<(arg?: any) => Promise<OptionsItem[]>>,
			default: null,
		},
		// api params
		params: {
			type: [Object, Number, String] as PropType<Recordable | number | string>,
			default: '',
		},
		// support xxx.xxx.xx
		resultField: { type: String as PropType<string>, default: 'data' },
		labelField: { type: String as PropType<string>, default: 'label' },
		valueField: { type: String as PropType<string>, default: 'value' },
		immediate: { type: Boolean as PropType<boolean>, default: true },
		options: { type: Array, default: () => [] },
		alwaysLoad: { type: Boolean as PropType<boolean>, default: false },
		afterFetch: { type: Function as PropType<Fn> },
	},
	emits: ['options-change', 'change', 'label-change'],
	setup(props, { emit }) {
		const options = ref<OptionsItem[]>([]);
		const loading = ref(false);
		const isFirstLoad = ref(true);
		const attrs = useAttrs() as Recordable;

		const getOptions = computed(() => {
			const { labelField, valueField, numberToString } = props;

			return unref(options).reduce((prev, next: Recordable) => {
				if (next) {
					const value = next[valueField];
					prev.push({
						...omit(next, [labelField, valueField]),
						label: next[labelField],
						value: numberToString ? `${value}` : value,
					});
				}
				return prev;
			}, [] as OptionsItem[]);
		});

		// immediate:true 在这里立即调用接口
		// const unwatch = watchEffect(() => {
		// 	props.immediate && !props.alwaysLoad && fetch();
		// });
		// unwatch();

		// 这边改为在Mounted进行，需测试
		onMounted(() => {
			props.immediate && !props.alwaysLoad && fetch();
		});

		watch(
			() => props.params,
			() => {
				!unref(isFirstLoad) && fetch();
			},
			{ deep: true },
		);

		watch(
			() => props.options,
			() => {
				setOptions(props.options as OptionsItem[]);
			},
		);

		function setOptions(option: OptionsItem[]) {
			options.value = option;
		}

		async function fetch() {
			const { api, afterFetch } = props;
			if (!api || !isFunction(api)) return;
			options.value = [];
			try {
				loading.value = true;
				let res = await api(props.params);
				if (afterFetch && isFunction(afterFetch)) {
					res = afterFetch(res);
				}
				if (Array.isArray(res)) {
					options.value = res;
					emitChange();
					return;
				}
				if (props.resultField) {
					options.value = get(res, props.resultField) || [];
				}
				emitChange();
			} catch (error) {
				console.warn(error);
			} finally {
				loading.value = false;
			}
		}

		async function handleFetch(visible: boolean) {
			if (visible) {
				if (props.alwaysLoad) {
					await fetch();
				} else if (!props.immediate && unref(isFirstLoad)) {
					await fetch();
					isFirstLoad.value = false;
				}
			}
		}

		function emitChange() {
			emit('options-change', unref(getOptions));
		}

		function handleChange(value, ...args) {
			emit('change', value, ...args);
		}

		return {
			attrs,
			getOptions,
			loading,
			handleFetch,
			handleChange,
		};
	},
});
</script>
